package com.ruoyi.csdn.socket;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.aes.AESEncryptor;
import com.ruoyi.csdn.domain.CsdnArticle;
import com.ruoyi.csdn.domain.CsdnLog;
import com.ruoyi.csdn.domain.CsdnUser;
import com.ruoyi.csdn.service.ICsdnArticleService;
import com.ruoyi.csdn.service.ICsdnLogService;
import com.ruoyi.csdn.service.ICsdnUserService;
import jakarta.annotation.Resource;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @Description
 * @Author Admin小闫
 * @Date 2024/12/30
 */
@ServerEndpoint("/WebSocketServer/{sid}")
@Component
public class WebSocketServer {

    private static ICsdnUserService csdnUserService;

    @Autowired
    public void setICsdnUserService(ICsdnUserService csdnUserService) {
        WebSocketServer.csdnUserService = csdnUserService;
    }

    private static AESEncryptor aesEncryptor;

    @Autowired
    public void setAesEncryptor(AESEncryptor aesEncryptor) {
        WebSocketServer.aesEncryptor = aesEncryptor;
    }

    private static ICsdnArticleService icsdnArticleService;

    @Autowired
    public void setAesEncryptor(ICsdnArticleService icsdnArticleService) {
        WebSocketServer.icsdnArticleService = icsdnArticleService;
    }

    private static ICsdnLogService ICsdnLogService;

    @Autowired
    public void setICsdnLogService(ICsdnLogService iCsdnLogService) {
        WebSocketServer.ICsdnLogService = iCsdnLogService;
    }

    // 日志对象
    private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);

    // 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    // concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
    // private static ConcurrentHashMap<String,WebSocketServer> websocketList = new ConcurrentHashMap<>();

    // 与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    // 接收sid
    private String sid = "";


    /*
     * 客户端创建连接时触发
     * */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this); // 加入set中
        addOnlineCount(); // 在线数加1
        log.info("有新窗口开始监听:" + sid + ", 当前在线人数为" + getOnlineCount());
        this.sid = sid;
    }

    /**
     * 客户端连接关闭时触发 【链接断开：删除CSDN表的clintID】
     **/
    @OnClose
    public void onClose() {
        webSocketSet.remove(this); // 从set中删除
        subOnlineCount(); // 在线数减1
        log.info("有一连接" + this.sid + "关闭！当前在线人数为" + getOnlineCount());
        //连接关闭的时候根据sid修改用户表的客户端id为""
        CsdnUser csdnUser = new CsdnUser();
        csdnUser.setClient(sid);
        List<CsdnUser> csdnUsers = csdnUserService.selectCsdnUserList(csdnUser);
        if (!csdnUsers.isEmpty()) {
            CsdnUser csdnUser1 = csdnUsers.get(0);
            csdnUser1.setClient("");
            csdnUserService.updateCsdnUser(csdnUser1);
        }
    }

    /**
     * 接收到客户端消息时触发【根据接受到的消息类型返回对应操作】
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自窗口" + sid + "的信息:" + message);
        //解析消息
        JSONObject jsonObject = JSONObject.parseObject(message);
        //消息类型
        int type = Integer.parseInt(jsonObject.getString("type"));
        if (type == 0) {
            log.info("收到登录类型消息: {}", jsonObject);
            String s = (String) jsonObject.get("message");
            if (s.equals("等待登录")) {
                //读取用户表分配用户
                CsdnUser csdnUser = csdnUserService.selectCsdnUserByClientIsNull();
                if (csdnUser == null) {
                    log.info("没有查询到空闲用户信息");
                    return;
                }
                log.info("查询到用户信息:{}", csdnUser);
                JSONObject jsonData = new JSONObject();
                jsonData.put("type", 0);
                jsonData.put("userid", csdnUser.getCsdnUserId());//账号：手机号
                jsonData.put("password", aesEncryptor.decrypt(csdnUser.getCsdnPassword()));//密码
                jsonData.put("username", csdnUser.getCsdnUsername());//用户名 ：L_7luo
                try {
                    //发送用户信息
                    sendInfo(JSON.toJSONString(jsonData), sid);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //修改用户表，设置客户端sid
                csdnUser.setClient(sid);
                csdnUserService.updateCsdnUser(csdnUser);
            } else if (s.equals("存在用户信息")) {
                //存在用户信息，更新用户表客户端id
                log.info("{},存在用户信息", sid);
                CsdnUser csdnUser = new CsdnUser();
                csdnUser.setCsdnUsername((String) jsonObject.get("userName"));
                List<CsdnUser> csdnUsers = csdnUserService.selectCsdnUserList(csdnUser);
                if (!csdnUsers.isEmpty()) {
                    CsdnUser csdnUser1 = csdnUsers.get(0);
                    csdnUser1.setClient(sid);
                    csdnUserService.updateCsdnUser(csdnUser1);
                }
            }
        } else if (type == 1) {
            log.info("收到文章发布类型消息: {}", jsonObject);
            if (jsonObject.getString("message").equals("发布成功")) {
                //收到发布成功消息
                log.info("文章: {},用户名: {}", jsonObject.getString("data"), jsonObject.getString("userName"));
                CsdnArticle csdnArticle = new CsdnArticle();
                csdnArticle.setCsdnUsername((String) jsonObject.get("userName"));
                JSONObject parse = (JSONObject) jsonObject.get("data");
                JSONObject m = (JSONObject) parse.get("message");
                Integer id = (Integer) m.get("id");
                csdnArticle.setId(Long.valueOf(id));
                List<CsdnArticle> csdnArticles = icsdnArticleService.selectCsdnArticleList(csdnArticle);
                if (!csdnArticles.isEmpty()) {
                    CsdnArticle csdnArticle1 = csdnArticles.get(0);
                    csdnArticle1.setReleaseStatus("1");
                    icsdnArticleService.updateCsdnArticle(csdnArticle1);
                }
            }else if (jsonObject.getString("message").equals("复制链接")) {
                log.info("收到复制链接类型消息: {}", jsonObject);
                CsdnLog csdnLog = new CsdnLog();
                csdnLog.setCsdnUsername((String) jsonObject.get("userName"));
                csdnLog.setArticleLink((String) jsonObject.get("data"));
                ICsdnLogService.insertCsdnLog(csdnLog);
            }
        }
    }

    /**
     * 连接发生异常时候触发
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送(向浏览器发消息)
     */
    public void sendMessage(String message) throws IOException {
        log.info("服务器消息推送：" + message);
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 发送消息到所有客户端
     * 指定sid则向指定客户端发消息
     * 不指定sid则向所有客户端发送消息
     */
    public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
        log.info("推送消息到窗口" + sid + "，推送内容:" + message);
        for (WebSocketServer item : webSocketSet) {
            try {
                // 这里可以设定只推送给这个sid的，为null则全部推送
                if (sid == null) {
                    item.sendMessage(message);
                } else if (item.sid.equals(sid)) {
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

    public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
        return webSocketSet;
    }

}
